﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Globalization;
using System.Linq;
using UnityEngine;

namespace GFramework
{
	public enum OriginAnchor
	{
		UpperLeft = 0,
		UpperCenter = 1,
		UpperRight = 2,
		MiddleLeft = 3,
		MiddleCenter = 4,
		MiddleRight = 5,
		LowerLeft = 6,
		LowerCenter = 7,
		LowerRight = 8,
	}

	public enum JobStatus
	{ 
		NewJob = 0,
		Wait,	
	}

	public enum NotifyType
	{ 
		Energy=0,
		Xu,
		Gold,
		Exp,
		Fashion,
		Collectible,
        Statima,
        BlackHeart,
        RedHeart,
        CoupleHeart,
        Rich,
        ClotherDraw,
        ReGacha,
        EnergyGacha,
	}

	[Flags]
	public enum StringMatchOptions
	{
		Full = 0x00,
		StartWith = 0x01,
		EndWith = 0x02,
		IgnoreCase = 0x04,
	}

	[Flags]
	public enum SetParentOptions
	{
		None = 0x00,
		PreserveLocalTransform = 0x01,
		SetParentLayer = 0x02,
	}


	public class Utils
	{
		#region [ Data Type ]

		/// <summary>
		/// Append array
		/// </summary>
		public static T[] AppendArray<T>(T[] array, T element)
		{
			T[] localArray = new T[(array == null) ? 1 : (array.Length + 1)];
			array.CopyTo(localArray, 0);
			localArray[localArray.Length - 1] = element;
			return localArray;
		}

		/// <summary>
		/// Display bit field
		/// </summary>
		/// <param name="array"></param>
		/// <returns></returns>
		public static string BitFieldDisplay(BitArray array)
		{
			return BitFieldDisplay(array, false);
		}

		public static string BitFieldDisplay(BitArray array, bool fullDisplay)
		{
			StringBuilder builder = new StringBuilder(string.Empty);
			foreach (bool bit in array)
			{
				builder.Append(!bit ? "0" : "1");
			}
			return builder.ToString();
		}

		public static bool ColorEqual(Color colorA, Color colorB)
		{
			return ((((colorA.r == colorB.r) && (colorA.g == colorB.g)) && (colorA.b == colorB.b)) && (colorA.a == colorB.a));
		}

		public static Color ColorFromBytes(byte r, byte g, byte b, byte a)
		{
			return new Color(((float)r) / 255f, ((float)g) / 255f, ((float)b) / 255f, ((float)a) / 255f);
		}
		public static Color ColorFromInt(int color)
		{
			byte a = (byte)((color & 0xff000000) >> 24);
			byte r = (byte)((color & 0x00ff0000) >> 16);
			byte g = (byte)((color & 0x0000ff00) >> 8);
			byte b = (byte)((color & 0x000000ff));

			return new Color(((float)r) / 255f, ((float)g) / 255f, ((float)b) / 255f, ((float)a) / 255f);
		}

		/// <summary>
		/// Parse tring to bool
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static bool ParseAsBool(string value)
		{
			bool flag;
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			if (value.Trim() == "1")
			{
				return true;
			}
			bool.TryParse(value, out flag);
			return flag;
		}

		public static System.Object ParseStringToValue(Type type, string val)
		{
			if (string.IsNullOrEmpty(val))
			{
				return null;
			}

			if (typeof(float).Equals(type))
			{
				return float.Parse(val, CultureInfo.InvariantCulture);
			}
			if (typeof(int).Equals(type))
			{
				return int.Parse(val, CultureInfo.InvariantCulture);
			}
			if (typeof(bool).Equals(type))
			{
				return ParseAsBool(val);
			}
			if (typeof(string).Equals(type))
			{
				return val;
			}
			if (type.IsEnum)
			{
				return Enum.Parse(type, val);
			}
			Debug.LogWarning(type + ": " + val);
			return null;
		}

		/// <summary>
		/// Check if collection is the same, ignore order 
		/// </summary>
		public static bool IsArrayTheSameIgnoreOrder<TType>(TType[] left, TType[] right, Func<TType, TType, bool> compare)
		{
			if (left == null && right == null)
				return true;

			if (left == null || right == null)
				return false;

			if (left.Length != right.Length)
				return false;

			for (int c = 0; c < left.Length; c++)
			{
				bool found = false;
				for (int n = 0; n < right.Length; n++)
				{
					if (compare(left[c], right[n]))
					{
						found = true;
					}
				}

				if (!found)
					return false;
			}

			return true;
		}

		#endregion

		#region [ GameObject and Components ]

		/// <summary>
		/// Sets the parent preserve local.
		/// </summary>
		public static void SetParentPreserveLocal(Transform parent, Transform child)
		{
			Vector3 localPosition = child.localPosition;
			Vector3 localScale = child.localScale;
			Quaternion localRotation = child.localRotation;
			child.parent = parent;
			child.localPosition = localPosition;
			child.localScale = localScale;
			child.localRotation = localRotation;
		}

		public static void SetParentPreserveLocal(GameObject parent, GameObject child)
		{
			SetParentPreserveLocal(parent.transform, child.transform);
		}

		/// <summary>
		/// Set parent with fully customizable options
		/// </summary>
		public static void SetParent(Transform parent, Transform child, SetParentOptions options)
		{
			if ((options & SetParentOptions.PreserveLocalTransform) == SetParentOptions.PreserveLocalTransform)
			{
				Vector3 localPosition = child.localPosition;
				Vector3 localScale = child.localScale;
				Quaternion localRotation = child.localRotation;
				child.parent = parent;
				child.localPosition = localPosition;
				child.localScale = localScale;
				child.localRotation = localRotation;
			}
			else
				child.parent = parent;

			if ((options & SetParentOptions.SetParentLayer) == SetParentOptions.SetParentLayer)
				SetLayerTree(child, parent.gameObject.layer);
		}

		public static void SetParent(GameObject parent, GameObject child, SetParentOptions options)
		{
			SetParent(parent.transform, child.transform, options);
		}

		/// <summary>
		/// Set layer for all object and its children
		/// </summary>
		public static void SetLayerTree(Transform trans, int layer)
		{
			trans.gameObject.layer = layer;
			foreach (Transform child in trans)
			{
				SetLayerTree(child, layer);
			}
		}

		public static void SetLayerTree(GameObject go, int layer)
		{
			SetLayerTree(go.transform, layer);
		}

		/// <summary>
		/// Destroys all children.
		/// </summary>
		public static void DestroyAllChildren(Transform trans)
		{
			if (trans.childCount > 0)
			{
				Transform[] children = trans.Cast<Transform>().ToArray();
				if (Application.isEditor)
				{
					for (int i = 0; i < children.Length; i++)
						UnityEngine.Object.DestroyImmediate(children[i].gameObject);
				}
				else
				{
					for (int i = 0; i < children.Length; i++)
						UnityEngine.Object.Destroy(children[i].gameObject);
				}
			}
		}

		public static void DestroyAllChildren(GameObject go)
		{
			DestroyAllChildren(go.transform);
		}

        /// <summary>
        /// Destroy all Children by Name
        /// </summary>
        /// <param name="go"></param>
        /// <param name="childName"></param>
        public static void DestroyAllChildrenByName(GameObject go, string childName)
        {
            Transform trans = go.transform;
            if (trans.childCount > 0)
            {
                Transform[] children = trans.Cast<Transform>().ToArray();
                if (Application.isEditor)
                {
                    for (int i = 0; i < children.Length; i++)
                        if (children[i].gameObject.name.Equals(childName))
                            UnityEngine.Object.DestroyImmediate(children[i].gameObject);
                }
                else
                {
                    for (int i = 0; i < children.Length; i++)
                        if (children[i].gameObject.name.Equals(childName))
                            UnityEngine.Object.Destroy(children[i].gameObject);
                }
            }
        }

		/// <summary>
		/// Find child transform by name
		/// </summary>
		public static Transform FindChildByName(Transform parent, string name, StringMatchOptions options)
		{
			foreach (Transform child in parent)
			{
				Transform found = _FindChildByName(child, name, options);
				if (found != null)
					return found;
			}

			return null;
		}

		/// <summary>
		/// Find child transform by name
		/// </summary>
		public static Transform FindChildByName(Transform parent, string name)
		{
			return FindChildByName(parent, name, StringMatchOptions.Full);
		}

		private static Transform _FindChildByName(Transform curr, string name, StringMatchOptions options)
		{
			bool isMatch = false;
			StringComparison strComp = ((options & StringMatchOptions.IgnoreCase) == StringMatchOptions.IgnoreCase) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

			if ((options & StringMatchOptions.Full) == StringMatchOptions.Full)
				isMatch = curr.name.Equals(name, strComp);
			// Start with
			else if ((options & StringMatchOptions.StartWith) == StringMatchOptions.StartWith)
				isMatch = curr.name.StartsWith(name, strComp);
			// End with
			else if ((options & StringMatchOptions.EndWith) == StringMatchOptions.EndWith)
				isMatch = curr.name.EndsWith(name, strComp);
			if( isMatch )
				return curr;
			
			// Loop children
			foreach (Transform child in curr)
			{
				Transform found = FindChildByName(child, name, options);
				if (found != null)
					return found;
			}

			//Debug.LogError("Utils.FindChildByName(): Transform with " + name + " not found under " + parent.name);
			return null;
		}

		public static GameObject FindChildByName(GameObject parent, string name, StringMatchOptions options)
		{
			Transform found = FindChildByName(parent.transform, name, options);
			if (found != null)
				return found.gameObject;

			return null;
		}

		public static GameObject FindChildByName(GameObject parent, string name)
		{
			return FindChildByName(parent, name, StringMatchOptions.Full);
		}
		
		/// <summary>
		/// Find all children transform by name
		/// </summary>
		public static List<Transform> FindChildrenByName(Transform parent, string name, StringMatchOptions options)
		{
			List<Transform> result = new List<Transform>();
			foreach (Transform child in parent)
				_FindChildrenByName(child, name, options,result);
			return result;
		}

		public static List<Transform> FindChildrenByName(Transform parent, string name)
		{
			return FindChildrenByName(parent, name, StringMatchOptions.Full);
		}

		private static void _FindChildrenByName(Transform curr, string name, StringMatchOptions options,List<Transform> result)
		{
			bool isMatch = false;
			StringComparison strComp = ((options & StringMatchOptions.IgnoreCase) == StringMatchOptions.IgnoreCase) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
            if (options == StringMatchOptions.Full)
            {
                isMatch = curr.name.Equals(name, strComp);
            }
            // Start with
            else if (options == StringMatchOptions.StartWith)
            {
                isMatch = curr.name.StartsWith(name, strComp);
            }
            else if (options == StringMatchOptions.EndWith)
                isMatch = curr.name.EndsWith(name, strComp);

            if (isMatch)
            {
                result.Add(curr);
            }

			// Loop children
			foreach (Transform child in curr)
				_FindChildrenByName(child, name, options,result);
		}

		public static List<GameObject> FindChildrenByName(GameObject parent, string name, StringMatchOptions options)
		{
			return FindChildrenByName(parent.transform, name, options).ConvertAll(t => t.gameObject).ToList();
		}

		public static List<GameObject> FindChildrenByName(GameObject parent, string name)
		{
			return FindChildrenByName(parent, name, StringMatchOptions.Full);
		}

		/// <summary>
		/// Find all objects of type
		/// </summary>
		public static T[] FindObjectsOfType<T>() where T : class
		{
			return Array.ConvertAll<UnityEngine.Object, T>(UnityEngine.Object.FindObjectsOfType(typeof(T)), delegate(UnityEngine.Object o)
			{
				return (o as T);
			});
		}

		/// <summary>
		/// Find or add component
		/// </summary>
		public static TCom FindOrAddComponent<TCom>(GameObject obj) where TCom : Component
		{
			TCom component = obj.GetComponent<TCom>();
			if (component == null)
				component = obj.AddComponent(typeof(TCom)) as TCom;

			if (component == null)
			{
				Debug.LogWarning("FindOrAddComponent: " + typeof(TCom).ToString() + " component is not found");
				return null;
			}

			return component;
		}

		/// <summary>
		/// Destriy component
		/// </summary>
		public static void DestroyComponent<TCom>(GameObject obj) where TCom : Component
		{
			TCom component = obj.GetComponent<TCom>();
			if (component != null)
			{
				if (Application.isEditor)
					UnityEngine.Object.DestroyImmediate(component);
				else
					UnityEngine.Object.Destroy(component);
			}
		}

		/// <summary>
		/// Get single component
		/// </summary>
		public static T GetComponent<T>(string tag) where T : Component
		{
			GameObject go = GameObject.FindWithTag(tag);
			if (go == null)
			{
				Debug.Log("No GameObject tagged as " + tag + " was found.");
				return null;
			}
			return go.GetComponent<T>();
		}

		/// <summary>
		/// Gets the component in parent.
		/// </summary>
		public static T GetComponentInParent<T>(GameObject go) where T : Component
		{
			UnityEngine.Object component = null;
			while ((go != null) && (component == null))
			{
				component = go.GetComponent(typeof(T));
				if (component == null)
				{
					Transform parent = go.transform.parent;
					go = (parent == null) ? null : parent.gameObject;
				}
			}
			return (component as T);
		}

		public static T GetComponentInParent<T>(Component c) where T : Component
		{
			return GetComponentInParent<T>(c.gameObject);
		}

		/// <summary>
		/// Gets the children recursive.
		/// </summary>
		public static List<Transform> GetChildrenRecursive(Transform parent)
		{
			List<Transform> results = new List<Transform>();
			foreach (Transform child in parent)
			{
				_GetChildrenRecursive(child, results);
			}
			return results;
		}

		private static void _GetChildrenRecursive(Transform obj, List<Transform> results)
		{
			results.Add(obj);

			foreach (Transform child in obj)
			{
				_GetChildrenRecursive(child, results);
			}
		}

		/// <summary>
		/// Gets the components in children recursive.
		/// </summary>
		public static List<TComp> GetComponentsInChildrenRecursive<TComp>(Transform obj) where TComp : Component
		{
			List<TComp> results = new List<TComp>();
			_GetComponentsInChildrenRecursive(obj, results);
			return results;
		}

		private static void _GetComponentsInChildrenRecursive<TComp>(Transform obj, List<TComp> results) where TComp : Component
		{
			var comp = obj.GetComponent<TComp>();
			if (comp != null)
				results.Add(comp);

			foreach (Transform child in obj)
			{
				_GetComponentsInChildrenRecursive(child, results);
			}
		}

		/// <summary>
		/// Get object full name (include parent path)
		/// </summary>
		public static string GetGameObjectFullName(GameObject go)
		{
			if (go == null)
			{
				return "";
			}

			Transform curr = go.transform;

			string str = "/" + curr.name;
			while (curr.parent != null)
			{
				curr = curr.parent;
				str = "/" + curr.name + str;
			}
			return str;
		}

		/// <summary>
		/// Get object full name (include parent path)
		/// </summary>
		public static string GetGameObjectRelativeName(GameObject go, GameObject relativeTo)
		{
			if (go == null)
			{
				return "";
			}

			Transform curr = go.transform;
			Transform relative = relativeTo == null ? null : relativeTo.transform;

			string str = string.Empty;
			while (curr != relative && curr != null)
			{
				if (str == string.Empty)
					str = curr.name;
				else
					str = curr.name + "/" + str;
				curr = curr.parent;
			}

			if (curr == null)
				str = "/" + str;

			return str;
		}

		/// <summary>
		/// Gets the full name of the object.
		/// </summary>
		public static string GetObjectFullName(UnityEngine.Object obj)
		{
			if (obj == null)
			{
				return "";
			}

			if (obj is Component)
			{
				Component component = obj as Component;
				return GetGameObjectFullName(component.gameObject) + "|" + component.GetType().Name;
			}
			if (obj is GameObject)
			{
				GameObject obj2 = obj as GameObject;
				return GetGameObjectFullName(obj2);
			}
			return "";
		}

		#endregion

		#region [ Useful Helpers ]

		/// <summary>
		/// Finds a camera which culling mask using specific layer.
		/// </summary>
		public static Camera FindCameraForLayer(int layer)
		{
			int layerMask = 1 << layer;

			Camera[] cameras = GameObject.FindSceneObjectsOfType(typeof(Camera)) as Camera[];

			for (int i = 0, imax = cameras.Length; i < imax; ++i)
			{
				Camera cam = cameras[i];

				if ((cam.cullingMask & layerMask) != 0)
				{
					return cam;
				}
			}
			return null;
		}

		#endregion

		#region [ Asset Resources ]

		/// <summary>
		/// Clone mesh
		/// </summary>
		public static Mesh CloneMesh(Mesh mesh)
		{
			if (mesh == null)
				return null;

			Mesh newMesh = new Mesh();
            newMesh.name = mesh.name + "(Clone)";

			newMesh.vertices = mesh.vertices;
			newMesh.triangles = mesh.triangles;
			newMesh.uv = mesh.uv;
			newMesh.uv1 = mesh.uv2;
			newMesh.uv2 = mesh.uv2;
			newMesh.normals = mesh.normals;
			newMesh.tangents = mesh.tangents;
			newMesh.colors = mesh.colors;
			newMesh.bindposes = mesh.bindposes;
			newMesh.boneWeights = mesh.boneWeights;

			newMesh.name = mesh.name;
			//newMesh.hideFlags = mesh.hideFlags;
			newMesh.subMeshCount = mesh.subMeshCount;
			newMesh.RecalculateBounds();

			return newMesh;
		}

		#endregion

		#region [ Platform Utils ]

		/// <summary>
		/// Check webplayer
		/// </summary>
		public static bool IsWebPlayer
		{
			get
			{
				return ((Application.platform == RuntimePlatform.OSXWebPlayer) || (Application.platform == RuntimePlatform.WindowsWebPlayer));
			}
		}

		public static string EscapeTextForJavascript(string text)
		{
			StringReader sr = new StringReader(text);
			StringBuilder content = new StringBuilder();
			string line = sr.ReadLine();
			while (line != null)
			{
				content.Append(line + "\\n");
				line = sr.ReadLine();
			}
			return content.ToString();
		}

		public static void DownloadText(string filename, string text)
		{
#if UNITY_WEBPLAYER
			Application.ExternalEval(
@"
function fireClick(elem) {
	if(typeof elem == 'string') elem = document.getElementById(objID);
	if(!elem) return;

	if(document.dispatchEvent) {   // W3C
		var oEvent = document.createEvent( 'MouseEvents' );
		oEvent.initMouseEvent('click', true, true, window, 1, 1, 1, 1, 1, false, false, false, false, 0, elem);
		elem.dispatchEvent(oEvent);
	}
	else if(document.fireEvent) {   // IE, Chrome
		elem.click();
	}    
}
var a = document.createElement('a');
var content = '" + EscapeTextForJavascript(text) + @"';
try {
var blob = new Blob([content], {type: 'application/octet-stream'});
} catch (e) {
var bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder);
bb.append(content);
var blob = bb.getBlob('application/octet-stream'); // <-- Here's the Blob
}
a.href = (window.webkitURL || window.URL).createObjectURL(blob);
a.download = '" + filename + @"';
fireClick(a);");
#elif UNITY_EDITOR || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN
			Debug.Log("Write file to " + filename);
			System.IO.File.WriteAllText(filename, text);
#endif
		}

		public static void DownloadImagePNG(string filename, Texture2D texture)
		{
#if UNITY_WEBPLAYER
			Application.ExternalEval(
@"
function fireClick(elem) {
	if(typeof elem == 'string') elem = document.getElementById(objID);
	if(!elem) return;

	if(document.dispatchEvent) {   // W3C
		var oEvent = document.createEvent( 'MouseEvents' );
		oEvent.initMouseEvent('click', true, true, window, 1, 1, 1, 1, 1, false, false, false, false, 0, elem);
		elem.dispatchEvent(oEvent);
	}
	else if(document.fireEvent) {   // IE, Chrome
		elem.click();
	}    
}
var a = document.createElement('a');
a.href = 'data:image/png;base64,' + '" + System.Convert.ToBase64String(texture.EncodeToPNG()) + @"';
a.download = '" + filename + @"';
fireClick(a);");
#elif UNITY_EDITOR || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN
			System.IO.File.WriteAllBytes(filename, texture.EncodeToPNG());
#endif
		}

		public static void OpenBrowserPopup(string text)
		{
#if UNITY_WEBPLAYER
			string begin =
@"var yourDOCTYPE = ""<!DOCTYPE html>"";
var printPreview = window.open('about:blank', 'print_preview');
var printDocument = printPreview.document;
printDocument.open();
printDocument.write(yourDOCTYPE + ""<html><body><p>"");";
			string end =
@"printDocument.write(""</p></body></html>"");
printDocument.close();";

			StringReader sr = new StringReader(text);
			StringBuilder content = new StringBuilder();
			string line = sr.ReadLine();
			while (line != null)
			{
				content.Append(@"printDocument.write(""" + line + @"<br>"");");
				line = sr.ReadLine();
			}

			Application.ExternalEval(begin + content + end);
#endif
		}

		#endregion
	}
}